# Tiny的词法分析
from enum import Enum
import sys

# tokentype=["ENDFILE","ERROR","ERROR","IF","THEN","ELSE","END",""]
MaxTokenLen = 30
ScanDebug =True
eofflag=False

filename='sample.tny'
fp=open(filename,'rb')

def getNextChar() -> str:
    readone = fp.read(1)
    if readone:
        readone = readone.decode()
    else:
        readone = 'eof'
        global eofflag
        eofflag = True
    return readone

def ungetNextChar():
    if not eofflag:
        fp.seek(-1,1)


def ifReverseWord(string)->str:
    ReversedWord = ['if', 'then', 'else', 'end', 'repeat', 'until', 'read', 'write']
    if string not in ReversedWord:
        return 'id'
    else:
        return string

# 每次调用gettoken，返回一个token
def getToken() -> tuple:
    # 总共六个状态
    # States = Enum('state',('START','INASSIGN','INCOMMENT','INNUM','INID','DONE'))

    cur_token = ''
    tokenstring = ''
    cur_state = 'start'  # 记录当前状态

    while cur_state != 'done':
        c = getNextChar()
        if eofflag == True: break
        save = True
        # 判断当前字符是否作为一个有意义的token返回
        # 如果是1.空白等无意义字符 2.注释 3.eof 就不返回
        if cur_state == 'start': #一开始必定是start
            if c == ' ' or c == '\t' or c == '\n':
                save = False
            elif c.isdigit():
                cur_state = 'innum'
            elif c.isalpha():
                cur_state = 'inid'
            elif c == ':':
                cur_state = 'inassign'
            elif c == '{':
                save = False
                cur_state = 'incomment'
            else:
                cur_state = 'done'
                if c == 'eof':
                    save = False
                    cur_token = 'endfile'
                elif c == '=':
                    cur_token = 'cmpeq'
                elif c == '<':
                    cur_token = 'cmplt'
                elif c == '+':
                    cur_token = 'add'
                elif c == '-':
                    cur_token = 'sub'
                elif c == '*':
                    cur_token = 'mul'
                elif c == '/':
                    cur_token = 'over'
                elif c == '(':
                    cur_token = 'lparen'
                elif c == ')':
                    cur_token = 'rparen'
                elif c == ';':
                    cur_token = 'semi'
                else:
                    cur_token = 'error'

        elif cur_state == 'incomment':
            save = False
            if c == 'eof':
                state = 'done'
                cur_token = 'endfile'
            elif c == '}':
                cur_state = 'start'
        elif cur_state == 'inassign':
            cur_state = 'done'
            if c == '=':
                cur_token = 'assign'
            else:
                ungetNextChar()
                save = False
                cur_token='error'
        elif cur_state=='innum':
            if not c.isdigit():
                #当前数字token结束了，回退一个
                ungetNextChar()
                #这个回退的字符是不算在里面的
                save=False
                cur_state='done'
                cur_token='num'
            # 否则，state继续为innum不变，接着循环读字符
        elif cur_state=='inid':
            if not c.isalpha():
                ungetNextChar()
                save=False
                cur_state='done'
                cur_token='id'

        else:
            print('current state is '+cur_state)
            cur_state='done'
            cur_token='error'

        if save and len(tokenstring)<MaxTokenLen:
            tokenstring+=c

        #如果是id的话，还有可能是保留字
        if cur_state=='done' and cur_token=='id':
            cur_token=ifReverseWord(tokenstring)

    #一次状态机运行结束，准备返回token
    if ScanDebug:
            if cur_token:print(cur_token + '\t' + tokenstring)


    return cur_token, tokenstring

